package com.sl.ms.dispatch.mq;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.sl.ms.api.CourierFeign;
import com.sl.ms.base.api.common.MQFeign;
import com.sl.ms.work.api.PickupDispatchTaskFeign;
import com.sl.ms.work.domain.dto.CourierTaskCountDTO;
import com.sl.ms.work.domain.enums.pickupDispatchtask.PickupDispatchTaskType;
import com.sl.transport.common.constant.Constants;
import com.sl.transport.common.vo.CourierTaskMsg;
import com.sl.transport.common.vo.OrderMsg;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 订单业务消息，接收到新订单后，根据快递员的负载情况，分配快递员
 */
@Slf4j
@Component
public class OrderMQListener {

    @Resource
    private CourierFeign courierFeign;

    @Resource
    private MQFeign mqFeign;

    @Resource
    private PickupDispatchTaskFeign pickupDispatchTaskFeign;

    /**
     * 如果有多个快递员，需要查询快递员今日的取派件数，根据此数量进行计算
     * 计算的逻辑：优先分配取件任务少的，取件数相同地取第一个分配
     * <p>
     * 发送生成取件任务时需要计算时间差，如果小于2小时，实时发送；大于2小时，延时发送
     * 举例：
     * 1、现在10:30分，用户期望：11:00 ~ 12:00上门，实时发送
     * 2、现在10:30分，用户期望：13:00 ~ 14:00上门，延时发送，12点发送消息，延时1.5小时发送
     *
     * @param msg 消息内容
     */
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = Constants.MQ.Queues.DISPATCH_ORDER_TO_PICKUP_DISPATCH_TASK),
            exchange = @Exchange(name = Constants.MQ.Exchanges.ORDER_DELAYED, type = ExchangeTypes.TOPIC, delayed = Constants.MQ.DELAYED),
            key = Constants.MQ.RoutingKeys.ORDER_CREATE
    ))
    public void listenOrderMsg(String msg) {
        //{"orderId":123, "agencyId": 8001, "taskType":1, "mark":"带包装", "longitude":116.111, "latitude":39.00, "created":1654224658728, "estimatedStartTime": 1654224658728}
        log.info("接收到订单的消息 >>> msg = {}", msg);

        // 1.将json反序列化
        OrderMsg orderMsg = JSONUtil.toBean(msg, OrderMsg.class);

        // 2.获取网点id 获取经纬度
        Double latitude = orderMsg.getLatitude();
        Double longitude = orderMsg.getLongitude();
        Long agencyId = orderMsg.getAgencyId();
        // 3.远程调用快递员微服务  ==> 查询满足  作业范围 满足 排班的快递员  feign
        List<Long> courierIdList = courierFeign.queryCourierIdListByCondition(agencyId, longitude, latitude, LocalDateTimeUtil.toEpochMilli(orderMsg.getEstimatedEndTime()));

        // 4.设置取派件快递员id  = null
        Long selectedCourierId = null;
        if (CollUtil.isNotEmpty(courierIdList)) {
            // 5.如果查询到快递员id 不为 null 则要选择快递员  selectCourier()
            selectedCourierId = selectCourier(courierIdList, orderMsg.getTaskType());
            log.info("根据当日任务选出的快递员id：{}", selectedCourierId);
        }


        // 6.生成取件任务消息
        CourierTaskMsg courierTaskMsg = new CourierTaskMsg();
        courierTaskMsg.setCourierId(selectedCourierId);
        courierTaskMsg.setAgencyId(agencyId);
        courierTaskMsg.setTaskType(orderMsg.getTaskType());
        courierTaskMsg.setOrderId(orderMsg.getOrderId());
        courierTaskMsg.setMark(orderMsg.getMark());
        courierTaskMsg.setEstimatedEndTime(orderMsg.getEstimatedEndTime());
        courierTaskMsg.setInfo(orderMsg.getInfo());
        // 毫秒
        courierTaskMsg.setCreated(System.currentTimeMillis());


        // 7.判断预计结束时间和当前是否大于两个小时
        // 进行时间的判断
        long between = LocalDateTimeUtil.between(LocalDateTime.now(), orderMsg.getEstimatedEndTime(), ChronoUnit.MINUTES);
        int delay = Constants.MQ.DEFAULT_DELAY; //默认实时发送  默认为-1
        // 要大于两个小时   并且要是取件任务
        if (between > 120 && ObjectUtil.equal(orderMsg.getTaskType(), 1)) {
            // 如果大于两个小时就要设置延迟
            LocalDateTime sendDataTime = LocalDateTimeUtil.offset(orderMsg.getEstimatedEndTime(), -2, ChronoUnit.HOURS);
            // 毫秒  delay就是存的毫秒
            long between1 = LocalDateTimeUtil.between(LocalDateTime.now(), sendDataTime, ChronoUnit.MILLIS);
            delay = Convert.toInt(between1);
        }

        //TODO 待实现
        this.mqFeign.sendMsg(Constants.MQ.Exchanges.PICKUP_DISPATCH_TASK_DELAYED,
                Constants.MQ.RoutingKeys.PICKUP_DISPATCH_TASK_CREATE,
                courierTaskMsg.toJson(),
                delay);
    }

    /**
     * 选择一个快递员来
     *
     * @param courierIdList 快递员列个表
     * @param taskType      任务类型
     * @return 选中的快递员id
     */
    private Long selectCourier(List<Long> courierIdList, Integer taskType) {

        // 1.集合长度为1 直接范围第一个
        if (courierIdList.size() == 1) {
            return courierIdList.get(0);
        }

        // 2.查询快递员数量  以及他的任务数
        String dateStr = DateUtil.date().toDateStr();
        // 第三个是字符串日期
        List<CourierTaskCountDTO> courierTaskCountDTOS = pickupDispatchTaskFeign.findCountByCourierIds(courierIdList, PickupDispatchTaskType.codeOf(taskType), dateStr);

        if (CollUtil.isEmpty(courierTaskCountDTOS)) {
            //没有查到任务数量，默认给第一个快递员分配任务
            return courierIdList.get(0);
        }

        // 3.如果查到的快递员数量  和 参数快递员数量 不一致  需要做补0操作  因为查询的时候这个快递员没有快递 就不会封装他的信息
        // 因此需要补0的操作
        if (ObjectUtil.notEqual(courierTaskCountDTOS.size(), courierIdList.size())) {
            // 需要补齐
            // 遍历全部
            List<CourierTaskCountDTO> collect = courierIdList.stream().filter(courierId -> {
                int index = CollUtil.indexOf(courierTaskCountDTOS, courierTaskCountDTO -> {
                    // 这里是循环比较的
                    return ObjectUtil.equal(courierId, courierTaskCountDTO.getCourierId());
                });
                return index == -1;
            }).map(courierId -> {
                // 进行补0
                return CourierTaskCountDTO.builder()
                        .courierId(courierId)
                        .count(0L)
                        .build();

            }).collect(Collectors.toList());
            courierTaskCountDTOS.addAll(collect);

        }

        // 4.按照count升序排序 返回第一个
        //选中任务数最小的快递员进行分配
        CollUtil.sortByProperty(courierTaskCountDTOS, "count");
        return courierTaskCountDTOS.get(0).getCourierId();

    }
}
